package com.shockweb.common.utils.classmethod;

import java.beans.PropertyDescriptor;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

import org.apache.commons.beanutils.PropertyUtils;

import com.shockweb.common.serializable.SerializableObject;


/**
 * Class的方法工具类
 * @author: 彭明华
 * History:
 * 2017年5月19日  彭明华创建
 */
public final class ClassMethod {


    /**
     * 禁止构造方法
     */
    private ClassMethod() {
    }

    /**
     * 获取instance对象的get方法值
     * @param instance 对象实例
     * @param name get方法的名称
     * @return Object 返回get值
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     */
    public static Object getProperty(Object instance,String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        return PropertyUtils.getProperty(instance, name);
    }


    /**
     * 给instance实例的set方法赋值，并能对value做转换
     * @param instance 对象实例
     * @param name set方法的名称
     * @param value 对象值
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     * @throws InstantiationException
     */
    public static void setProperty(Object instance,String name,Object value) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, InstantiationException {
    	if(value==null){
    		PropertyUtils.setProperty(instance, name, null);
    	}else{
    		if(PropertyUtils.isWriteable(instance, name)){
				PropertyDescriptor descriptor = PropertyUtils.getPropertyDescriptor(instance, name);
				Method m = PropertyUtils.getWriteMethod(descriptor);
				Class<?>[] clazz = m.getParameterTypes();
				try {
					m.invoke(instance, new Object[]{SerializableObject.objectConvert(clazz[0],value,m.getAnnotation(GenericType.class),0)});
				} catch (Exception e) {
					throw new InvocationTargetException(e);
				}
    		}
    	}
    }
    
    
	/**
	 * 缓存Object对象的get名
	 */
    private static List<String> noGetMethodList = null;
    
    /**
     * 查询出Object的get方法
     */
    private static void initNoGetMethodList(){
    	List<String> init = new ArrayList<String>();
        for(Method method : Object.class.getMethods())
        {
            String name = method.getName();
			if(name.length()>3 && name.startsWith("get")
					&& method.getParameterTypes().length==0
					&& !method.getReturnType().equals(void.class)){
				init.add(name);
			}else if(name.length()>2 && name.startsWith("is")
					&& method.getParameterTypes().length==0
					&& method.getReturnType().equals(boolean.class)){
				init.add(name);
			}
        }
        noGetMethodList = init;
    }
    
    /**
     * 判断methodName方法为Object，如果是表示非自定义get方法
     * @param methodName 方法名
     * @return boolean 如果是定义方法返回true,否则为false
     */
    private static boolean isGetMethod(String methodName){
    	if(noGetMethodList == null)
    		initNoGetMethodList();
    	boolean rtn = true;
    	for(String name2:noGetMethodList){
    		if(methodName.equals(name2)){
    			rtn = false;
    			break;
    		}
    	}
    	return rtn;
    }
    
    /**
     * 缓存class的get方法
     */
    private static Map<Class<?>,List<ClassMethodDefine>> getMethods = new HashMap<Class<?>,List<ClassMethodDefine>>();
    
    /**
     * 返回cls所有get方法
     * @param cls class定义
     * @return List get方法列表
     */
    public static List<ClassMethodDefine> getGetMethods(Class<?> cls){
    	synchronized(getMethods){
	    	if(!getMethods.containsKey(cls)){
	            ArrayList<ClassMethodDefine> tmp = new ArrayList<ClassMethodDefine>();
	            for(Method method : cls.getMethods())
	            {
	                String name = method.getName();
	    			if(name.length()>3 && name.startsWith("get")
	    					&& method.getParameterTypes().length==0
	    					&& !method.getReturnType().equals(void.class)
	    					&& isGetMethod(name)){
	    				name = name.substring(3,4).toLowerCase() + name.substring(4, name.length());
	    				tmp.add(new ClassMethodDefine(name,method));
	    			}else if(name.length()>2 && name.startsWith("is")
	    					&& method.getParameterTypes().length==0
	    					&& method.getReturnType().equals(boolean.class)
	    					&& isGetMethod(name)){
	    				name = name.substring(2,3).toLowerCase() + name.substring(3, name.length());
	    				tmp.add(new ClassMethodDefine(name,method));
	    			}
	            }
	            getMethods.put(cls, tmp);
	    	}
	        return getMethods.get(cls);
    	}
    }

    /**
     * 缓存object所有set方法名
     */
    private static List<String> noSetMethodList = null;
    
    /**
     * 查询出Object的set方法
     */
    private static void initNoSetMethodList(){
    	List<String> init = new ArrayList<String>();
        for(Method method : Object.class.getMethods())
        {
            String name = method.getName();
			if(name.length()>3 && name.startsWith("set")
					&& method.getParameterTypes().length==0
					&& !method.getReturnType().equals(void.class)){
				init.add(name);
			}
        }
        noSetMethodList = init;
    }
    
    /**
     * 判断methodName方法为Object，如果是表示非自定义set方法
     * @param methodName 方法名
     * @return boolean 如果是定义方法返回true,否则为false
     */
    private static boolean isSetMethod(String methodName){
    	if(noSetMethodList == null)
    		initNoSetMethodList();
    	boolean rtn = true;
    	for(String name2:noSetMethodList){
    		if(methodName.equals(name2)){
    			rtn = false;
    			break;
    		}
    	}
    	return rtn;
    }
    
    /**
     * 缓存class的get方法
     */
    private static Map<Class<?>,List<ClassMethodDefine>> setMethods = new HashMap<Class<?>,List<ClassMethodDefine>>();
    
    
    /**
     * 返回cls所有set方法
     * @param cls class定义
     * @return List set方法列表
     */
    public static List<ClassMethodDefine> getSetMethods(Class<?> cls){
    	synchronized(setMethods){
    		if(!setMethods.containsKey(cls)){
    	        ArrayList<ClassMethodDefine> tmp = new ArrayList<ClassMethodDefine>();
    			for(Method method : cls.getMethods()){
    				String name = method.getName();
    				if(name.length()>3 && name.startsWith("set")
    						&& method.getParameterTypes().length==1
    						&& method.getReturnType().equals(void.class)
    						&& isSetMethod(name)){
    					name = name.substring(3,4).toLowerCase() + name.substring(4, name.length());
    					tmp.add(new ClassMethodDefine(name, method));
    				}
    			}
    			setMethods.put(cls, tmp);
    		}

	        return setMethods.get(cls);
    	}
    }
    
    /**
     * 缓存class的get方法
     */
    private static Map<Class<?>,Map<String,ClassMethodDefine>> setMethodsMap = new HashMap<Class<?>,Map<String,ClassMethodDefine>>();
    
    
    
    /**
     * 返回cls所有set方法，Map类型
     * @param cls class定义
     * @return Map set方法列表
     */
    public static Map<String,ClassMethodDefine> getSetMethodsMap(Class<?> cls){
    	synchronized(setMethodsMap){
    		if(!setMethodsMap.containsKey(cls)){
    	        HashMap<String,ClassMethodDefine> tmp = new HashMap<String,ClassMethodDefine>();
    			for(Method method : cls.getMethods()){
    				String name = method.getName();
    				if(name.length()>3 && name.startsWith("set")
    						&& method.getParameterTypes().length==1
    						&& method.getReturnType().equals(void.class)
    						&& isSetMethod(name)){
    					name = name.substring(3,4).toLowerCase() + name.substring(4, name.length());
    					tmp.put(name,new ClassMethodDefine(name, method));
    				}
    			}
    			setMethodsMap.put(cls, tmp);
    		}
    		return setMethodsMap.get(cls);
    	}
    }
    
    /**
     * 缓存class的set和get方法定义
     */
	private static Map<Class<?>,Map<String,List<ClassMethodParamDefine>>> classMethods = new HashMap<Class<?>,Map<String,List<ClassMethodParamDefine>>>();
	
	/**
	 * 通过方法名获取method定义.
	 * @param clazz class定义
	 * @param method 方法的名称
	 * @return List 返回方法的参数定义
	 * @throws SecurityException
	 * @throws NotFoundException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 */
	public static List<ClassMethodParamDefine> getMethods(Class<?> clazz,String method) throws SecurityException, NotFoundException, NoSuchMethodException, ClassNotFoundException{
		synchronized(classMethods){
			if(!classMethods.containsKey(clazz)){
				classMethods.put(clazz, getClassMethods(clazz));
			}
		}
		Map<String,List<ClassMethodParamDefine>> methods = classMethods.get(clazz);
		if(methods!=null){
			return methods.get(method);
		}
		return null;
	}

	
	/**
	 * 通过方法名和参数类型获取method
	 * @param clazz class定义
	 * @param method 方法的名称
	 * @param parameterTypes 方法的参数
	 * @return Method 方法
	 * @throws NotFoundException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 */
	public static Method getMethod(Class<?> clazz,String method,Class<?>... parameterTypes) throws NotFoundException,NoSuchMethodException, ClassNotFoundException{
		ClassMethodParamDefine define = getMethodDefine(clazz,method,parameterTypes);
		if(define!=null){
			return define.getMethod();
		}else{
			return null;
		}
	}

	/**
	 * 通过方法名和参数类型获取method定义
	 * @param clazz class定义
	 * @param method 方法的名称
	 * @param parameterTypes 方法的参数
	 * @return ClassMethodParamDefine 方法的完整定义
	 * @throws NotFoundException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 */
	public static ClassMethodParamDefine getMethodDefine(Class<?> clazz,String method,Class<?>... parameterTypes) throws NotFoundException,NoSuchMethodException, ClassNotFoundException{
		synchronized(classMethods){
			if(!classMethods.containsKey(clazz)){
				classMethods.put(clazz, getClassMethods(clazz));
			}
		}
		Map<String,List<ClassMethodParamDefine>> methods = classMethods.get(clazz);
		if(methods!=null){
			List<ClassMethodParamDefine> methodDefines = methods.get(method);
			if(methodDefines!=null){
				for(ClassMethodParamDefine define:methodDefines){
					if(equalsTypes(define.getParameterTypes(),parameterTypes)){
						return define;
					}
				}
			}
		}
		return null;
	}
	
	/**
	 * 通过方法名和参数名获取method，如果存在两个符合条件的方法，则获取第一个method定义
	 * @param clazz class定义
	 * @param method 方法的名称
	 * @param parameterNames 参数名称
	 * @return ClassMethodParamDefine 方法的完整定义
	 * @throws NotFoundException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 */
	public static ClassMethodParamDefine getMethodDefine(Class<?> clazz,String method,String... parameterNames) throws NotFoundException,NoSuchMethodException, ClassNotFoundException{
		String[] args = null;
		if(parameterNames!=null){
			int len=0;
			for(String name:parameterNames){
				if(name!=null && !name.trim().equals("")){
					len++;
				}
			}
			args = new String[len];
			int index=0;
			for(String name:parameterNames){
				if(name!=null && !name.trim().equals("")){
					args[index] = name;
					index++;
				}
			}
		}
		synchronized(classMethods){
			if(!classMethods.containsKey(clazz)){
				classMethods.put(clazz, getClassMethods(clazz));
			}
		}
		Map<String,List<ClassMethodParamDefine>> methods = classMethods.get(clazz);
		if(methods!=null){
			List<ClassMethodParamDefine> methodDefines = methods.get(method);
			if(methodDefines!=null){
				for(ClassMethodParamDefine define:methodDefines){
					if(equalsStrs(define.getParameterNames(),parameterNames)){
						return define;
					}
				}
			}
		}
		return null;
	}
	
	/**
	 * 通过方法名和参数数量获取method
	 * @param clazz class定义
	 * @param method 方法的名称
	 * @param parametersLength 方法的参数
	 * @return Method 方法
	 * @throws NotFoundException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 */
	public static Method getMethod(Class<?> clazz,String method,int parametersLength) throws NotFoundException,NoSuchMethodException, ClassNotFoundException{
		ClassMethodParamDefine define = getMethodDefine(clazz,method,parametersLength);
		if(define!=null){
			return define.getMethod();
		}else{
			return null;
		}
	}

	/**
	 * 通过方法名和参数量型获取method定义
	 * @param clazz class定义
	 * @param method 方法的名称
	 * @param parametersLength 方法的参数
	 * @return ClassMethodParamDefine 方法的完整定义
	 * @throws NotFoundException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 */
	public static ClassMethodParamDefine getMethodDefine(Class<?> clazz,String method,int parametersLength) throws NotFoundException,NoSuchMethodException, ClassNotFoundException{
		synchronized(classMethods){
			if(!classMethods.containsKey(clazz)){
				classMethods.put(clazz, getClassMethods(clazz));
			}
		}
		Map<String,List<ClassMethodParamDefine>> methods = classMethods.get(clazz);
		if(methods!=null){
			List<ClassMethodParamDefine> methodDefines = methods.get(method);
			if(methodDefines!=null){
				for(ClassMethodParamDefine define:methodDefines){
					if((parametersLength==0 && define.getParameterTypes()==null) ||  define.getParameterTypes().length == parametersLength){
						return define;
					}
				}
			}
		}
		return null;
	}
	
	/**
	 * 通过方法名和参数名获取method，如果存在两个符合条件的方法，则获取第一个方法
	 * @param clazz class定义
	 * @param method 方法名称
	 * @param parameterNames 方法的完整定义
	 * @return Method 方法
	 * @throws NotFoundException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 */
	public static Method getMethod(Class<?> clazz,String method,String... parameterNames) throws NotFoundException,NoSuchMethodException, ClassNotFoundException{
		ClassMethodParamDefine define = getMethodDefine(clazz,method,parameterNames);
		if(define!=null){
			return define.getMethod();
		}else{
			return null;
		}
	}
	
	/**
	 * 比较c1与c2的所有Class是否相等或者子类，c1可以是c2的父类
	 * @param c1 Class数组
	 * @param c2 Class数组
	 * @return boolean 相等返回true，否则返回false
	 */
	public static boolean equalsTypes(Class<?>[] c1,Class<?>[] c2){
		if(c1==null || c1.length==0){
			if(c2==null || c2.length==0){
				return true;
			}else{
				return false;
			}
		}else{
			if(c2==null || c2.length==0){
				return false;
			}else{
				if(c1.length!=c2.length){
					return false;
				}else{
					for(int i=0;i<c1.length;i++){
						if(!c1[i].isAssignableFrom(c2[i])){
							return false;
						}
					}
					return true;
				}
			}
		}
	}
	
	/**
	 * 比较arg1与arg2的字符数组是否相等
	 * @param arg1 String数组
	 * @param arg2 String数组
	 * @return boolean 相等返回true，否则返回false
	 */
	private static boolean equalsStrs(String[] arg1,String[] arg2){
		if(arg1==null || arg1.length==0){
			if(arg2==null || arg2.length==0){
				return true;
			}else{
				return false;
			}
		}else{
			if(arg2==null || arg2.length==0){
				return false;
			}else{
				if(arg1.length!=arg2.length){
					return false;
				}else{
					for(String arg:arg1){
						boolean flag = false;
						for(String a:arg2){
							if(arg.equals(a)){
								flag = true;
								break;
							}
						}
						if(!flag){
							return false;
						}
					}
					return true;
				}
			}
		}
	}
	
	/**
	 * 获取class的完整名称
	 * @param clazz Class定义
	 * @return String 返回Class的全路径名称
	 */
	public static String getClassName(Class<?> clazz){
		if(clazz.isArray()){
			return getClassName(clazz.getComponentType()) + "[]";
		}else{
			return clazz.getName();
		}
	}


	/**
	 * 获取Class的方法定义
	 * @param clazz Class定义
	 * @return Map<String,List<ClassMethodParamDefine>> 所有方法定义
	 * @throws NotFoundException
	 * @throws SecurityException 
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException 
	 */
	private static Map<String,List<ClassMethodParamDefine>> getClassMethods(Class<?> clazz) throws NotFoundException, SecurityException, NoSuchMethodException, ClassNotFoundException {
		Map<String,List<ClassMethodParamDefine>> mtds = new HashMap<String,List<ClassMethodParamDefine>>();
		Method[] ms = clazz.getMethods();
		ClassPool pool = ClassPool.getDefault();
		ClassClassPath clsPath = new ClassClassPath(clazz);
		try{
			pool.appendClassPath(clsPath);
			CtClass cc = pool.get(clazz.getName());
			CtMethod[] cms =  cc.getMethods();
			if(ms!=null && ms.length!=0){
				for(Method md:ms){
					List<ClassMethodParamDefine> methodDefines = null;
					String methodName = md.getName();
					if(mtds.containsKey(methodName)){
						methodDefines = mtds.get(md.getName());
					}else{
						methodDefines = new ArrayList<ClassMethodParamDefine>();
						mtds.put(methodName, methodDefines);
					}
					Class<?>[] parameterTypes = md.getParameterTypes();
					ClassMethodParamDefine define = null;
					if(parameterTypes==null || parameterTypes.length==0){
						define = new ClassMethodParamDefine(md,null,null);
					}else{
						String[] parameterNames = new String[parameterTypes.length];
						for(CtMethod cm:cms){
							if(Modifier.isPublic(cm.getModifiers()) && methodName.equals(cm.getName())){
								CtClass[] params = cm.getParameterTypes();
								if(parameterTypes==null || parameterTypes.length==0){
									if(params==null || params.length==0){
										break;
									}
								}else{
									if(params!=null && params.length==parameterTypes.length){
										boolean flag = true;
										for(int i=0;i<parameterTypes.length;i++){
											if(!getClassName(parameterTypes[i]).equals(params[i].getName())){
												flag = false;
												break;
											}
										}
										if(flag){
											MethodInfo methodInfo = cm.getMethodInfo();  
											CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
											if(codeAttribute!=null){
												LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
												int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
												for(int i=0;i<parameterTypes.length;i++){
													if(attr==null){
														parameterNames[i] = "arg" + i;
													}else{
														parameterNames[i] = attr.variableName(i + pos);
													}
												}
											}else{
												for(int i=0;i<parameterTypes.length;i++){
													parameterNames[i] = "arg" + i;
												}
											}
											define = new ClassMethodParamDefine(md,parameterNames,parameterTypes);
											break;
										}
									}
								}
							}
						}
					}
					if(define!=null)
						methodDefines.add(define);
				}
			}
		}finally{
			pool.removeClassPath(clsPath);
		}
		return mtds;
	}


	
	/**
	 * 比较两个方法定义
	 * @param src
	 * @param tag
	 * @return
	 */
	public static boolean equalsMethod(Class<?>[] src,Class<?>[] tag){
		if(src==null || src.length==0){
			if(tag==null || tag.length==0){
				return true;
			}else{
				return false;
			}
		}else{
			if(tag==null || tag.length==0){
				return false;
			}else{
				return SerializableObject.equals(src,tag);
			}
		}
	}

}
